package com.zst.amqp.utils;

import com.zst.amqp.annotation.Consumer;
import com.zst.amqp.entity.ConsumerInfo;
import com.zst.amqp.entity.MessageDTO;
import com.zst.amqp.exception.ConsumerNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;

import javax.el.MethodNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 根据ConsumerInfo从Spring上下文中获取消费者对象并调用消费者的工具类
 * @author zst
 */
public class ConsumerInvokeUtils {
    private static final Logger logger = LoggerFactory.getLogger(ConsumerInvokeUtils.class);
    /**
     * 根据ConsumerInfo调用消费者方法
     * @param consumerInfo
     * @param message
     */
    public static void invoke(ApplicationContext context, ConsumerInfo consumerInfo, MessageDTO message) throws Exception {
        if (consumerInfo == null) {
            throw new ConsumerNotFoundException("ConsumerInfo is null");
        } else if (consumerInfo.getConsumerClass() == null) {
            throw new ConsumerNotFoundException("Consumer class is null");
        }

        try {
            // 从spring上下文中获取消费者实例
            Object consumerObj = context.getBean(consumerInfo.getConsumerClass());
            Method[] methods = consumerInfo.getConsumerClass().getMethods();
            for (Method m : methods) {
                if (checkMethodIsConsumer(m, consumerInfo)) {
                    m.invoke(consumerObj, message.getContent());
                    break;
                }
            }

        } catch (NoSuchBeanDefinitionException ne) {
            throw new ConsumerNotFoundException("Consumer bean not found");
        } catch (MethodNotFoundException e) {
            throw new ConsumerNotFoundException("Consumer Method Not Found:"
                    + consumerInfo.getConsumerClass().getName() + "." + consumerInfo.getMethodName());
        } catch (IllegalAccessException | InvocationTargetException ex) {
            throw ex;
        }
    }

    /**
     * 通过检查目标方法注解是否存在，注解的msgType值是否和消费者信息中的消息类型相同，
     * 以及方法名称和消费者方法名称是否相同来判断目标方法是否是消费者对应的方法
     * @param method
     * @param consumerInfo
     * @return
     */
    private static boolean checkMethodIsConsumer(Method method, ConsumerInfo consumerInfo) {
        Consumer consumerAnnotation = method.getAnnotation(Consumer.class);
        return method.getAnnotation(Consumer.class) != null
                && consumerAnnotation.msgType().equals(consumerInfo.getMsgType())
                && method.getName().equals(consumerInfo.getMethodName());
    }
}
